# -*-coding: utf-8 -*-
"""
作者：TianyiFan
日期：2023-年11-月13日
用途：benders 对偶模型
"""
import para as params
from gurobipy import *
import time
import numpy as np


def dual_SP(gamma_ik):
    time_start = time.time()
    model = Model("DSP_model")
    model.setParam("OutputFlag", 0)
    # region 定义决策变量
    pai_i_1 = {}
    pai_i_2 = {}
    pai_i_3 = {}
    pai_i_4 = {}
    pai_ik_5 = {}
    pai_ik_6 = {}
    pai_ik_7 = {}
    pai_t_8 = {}
    pai_ih_9 = {}
    pai_ih_10 = {}
    pai_i_11 = {}
    pai_i_12 = {}
    pai_i_13 = {}
    pai_i_14 = {}
    for i in range(params.V + 2):
        pai_i_1[i] = model.addVar(vtype=GRB.CONTINUOUS,name="pai_1_{}".format(i))
        pai_i_2[i] = model.addVar(vtype=GRB.CONTINUOUS,name="pai_2_{}".format(i))
        pai_i_3[i] = model.addVar(vtype=GRB.CONTINUOUS,name="pai_3_{}".format(i))
        pai_i_4[i] = model.addVar(vtype=GRB.CONTINUOUS,name="pai_4_{}".format(i))
        pai_i_11[i] = model.addVar(ub=0,vtype=GRB.CONTINUOUS,name="pai_11_{}".format(i))
        pai_i_12[i] = model.addVar(ub=0,vtype=GRB.CONTINUOUS,name="pai_12_{}".format(i))
        pai_i_13[i] = model.addVar(ub=0,vtype=GRB.CONTINUOUS,name="pai_13_{}".format(i))
        pai_i_14[i] = model.addVar(ub=0,vtype=GRB.CONTINUOUS,name="pai_14_{}".format(i))
        for k in range(params.K1):
            pai_ik_5[i, k] = model.addVar(ub=0,vtype=GRB.CONTINUOUS,name="pai_5_{}_{}".format(i,k))
            pai_ik_6[i, k] = model.addVar(ub=0,vtype=GRB.CONTINUOUS,name="pai_6_{}_{}".format(i,k))
            pai_ik_7[i, k] = model.addVar(ub=0,vtype=GRB.CONTINUOUS,name="pai_7_{}_{}".format(i,k))
        for h in range(params.H):
            pai_ih_9[i, h] = model.addVar(ub=0,vtype=GRB.CONTINUOUS,name="pai_9_{}_{}".format(i,h))
            pai_ih_10[i, h] = model.addVar(ub=0,vtype=GRB.CONTINUOUS,name="pai_10_{}_{}".format(i,h))
    for t in range(params.T):
        pai_t_8[t] = model.addVar(ub=0,vtype=GRB.CONTINUOUS,name="pai_8_{}".format(t))
    # endregion

    # region 定义目标函数
    obj = LinExpr(0)
    for i in range(1,params.V+1):
        obj.addTerms(params.c_ui[i], pai_i_1[i])
        obj.addTerms(params.c_ui[i], pai_i_3[i])
        obj.addTerms(params.c_li[i], pai_i_2[i])
        obj.addTerms(params.c_li[i], pai_i_4[i])
        obj.add(quicksum(params.z * gamma_ik[i, k] * (pai_ik_5[i, k] + pai_ik_6[i, k] + pai_ik_7[i, k]) for k in range(params.K1)))
    obj.add(quicksum(params.C*pai_t_8[t] for t in range(params.T)))
    model.setObjective(obj, GRB.MAXIMIZE)
    # endregion

    # region 定义约束条件
    for i in range(1,params.V+1):
        for h in range(params.H):
            model.addConstr(pai_i_1[i] + pai_ih_9[i, h] <= 0,name="dual_constrain_1_{}_{}".format(i,h))
            model.addConstr(pai_i_2[i] + pai_ih_10[i, h] <= 0,name="dual_constrain_2_{}_{}".format(i,h))
    for i in range(1,params.V+1):
        model.addConstr(pai_i_12[i] - pai_i_11[i] >= 0,name="dual_constrain_3_{}".format(i))
        model.addConstr(pai_i_14[i] - pai_i_13[i] >= 0,name="dual_constrain_4_{}".format(i))
        model.addConstr(pai_i_12[i] - pai_i_11[i] <= 0,name="dual_constrain_5_{}".format(i))
        model.addConstr(pai_i_14[i] - pai_i_13[i] <= 0,name="dual_constrain_6_{}".format(i))
        for k in range(params.K1):
            model.addConstr(params.t_lki[k, i] - pai_i_4[i] - pai_ik_5[i, k] - pai_ik_7[i, k] >= 0,name="dual_constrain_7_{}_{}".format(i,k))
            model.addConstr(params.t_uki[k, i] - pai_i_3[i] - pai_ik_5[i, k] - pai_ik_6[i, k] >= 0,name="dual_constrain_8_{}_{}".format(i,k))
    for i in range(1,params.V+1):
        expr1 = quicksum(pai_ih_10[i,h] for h in range(params.H))
        expr2 = quicksum(pai_ih_9[i,h] for h in range(params.H))
        model.addConstr(params.time_p + expr1 >= 0,name="dual_constrain_9_{}".format(i))
        model.addConstr(params.time_p + expr2 >= 0,name="dual_constrain_10_{}".format(i))
        expr3 = LinExpr()
        for t in range(params.T):
            if params.l_it[i,t]==1:
                expr3.addTerms(1/params.handle_i[i],pai_t_8[t])
        model.addConstr(params.time_u + pai_i_13[i] + pai_i_14[i]-expr3 >= 0,name="dual_constrain_11_{}".format(i))
        model.addConstr(params.time_u + pai_i_11[i] + pai_i_12[i]-expr3 >= 0,name="dual_constrain_12_{}".format(i))
    # endregion

    model.update()

    model.optimize()
    time_end = time.time()
    print("DSP_model 耗时：{:.2f}s".format(time_end - time_start))
    print("模型的求解状态：", model.getAttr(GRB.attr.Status))
    if model.getAttr(GRB.attr.Status) in [2,9]:
        print("DSP - obj = {} ".format(model.ObjVal))
    else:
        print('Optimization was stopped with status =  %d' % model.getAttr(GRB.attr.Status))
        model.computeIIS()
        for c in model.getConstrs():
            if c.IISConstr:
                print('%s' % c.constrName)

def dual_SP_v2(gamma_ik):
    time_start = time.time()
    model = Model("DSP_model")
    model.setParam("OutputFlag", 0)
    # region 定义决策变量
    pai_i_1 = {}
    pai_i_2 = {}
    pai_i_3 = {}
    pai_i_4 = {}
    pai_ik_5 = {}
    pai_ik_6 = {}
    pai_ik_7 = {}
    pai_t_8 = {}
    pai_ih_9 = {}
    pai_ih_10 = {}
    pai_i_11 = {}
    pai_i_12 = {}
    pai_i_13 = {}
    pai_i_14 = {}
    for i in range(params.V + 2):
        pai_i_1[i] = model.addVar(vtype=GRB.CONTINUOUS,name="pai_1_{}".format(i))
        pai_i_2[i] = model.addVar(vtype=GRB.CONTINUOUS,name="pai_2_{}".format(i))
        pai_i_3[i] = model.addVar(vtype=GRB.CONTINUOUS,name="pai_3_{}".format(i))
        pai_i_4[i] = model.addVar(vtype=GRB.CONTINUOUS,name="pai_4_{}".format(i))
        pai_i_11[i] = model.addVar(ub=0,vtype=GRB.CONTINUOUS,name="pai_11_{}".format(i))
        pai_i_12[i] = model.addVar(ub=0,vtype=GRB.CONTINUOUS,name="pai_12_{}".format(i))
        pai_i_13[i] = model.addVar(ub=0,vtype=GRB.CONTINUOUS,name="pai_13_{}".format(i))
        pai_i_14[i] = model.addVar(ub=0,vtype=GRB.CONTINUOUS,name="pai_14_{}".format(i))
        for k in range(params.K1):
            pai_ik_5[i, k] = model.addVar(ub=0,vtype=GRB.CONTINUOUS,name="pai_5_{}_{}".format(i,k))
            pai_ik_6[i, k] = model.addVar(ub=0,vtype=GRB.CONTINUOUS,name="pai_6_{}_{}".format(i,k))
            pai_ik_7[i, k] = model.addVar(ub=0,vtype=GRB.CONTINUOUS,name="pai_7_{}_{}".format(i,k))
        for h in range(params.H):
            pai_ih_9[i, h] = model.addVar(ub=0,vtype=GRB.CONTINUOUS,name="pai_9_{}_{}".format(i,h))
            pai_ih_10[i, h] = model.addVar(ub=0,vtype=GRB.CONTINUOUS,name="pai_10_{}_{}".format(i,h))
    for t in range(params.T):
        pai_t_8[t] = model.addVar(ub=0,vtype=GRB.CONTINUOUS,name="pai_8_{}".format(t))
    # endregion

    # region 定义目标函数
    obj = LinExpr(0)
    for i in range(1,params.V+1):
        obj.addTerms(params.c_ui[i], pai_i_1[i])
        obj.addTerms(params.c_ui[i], pai_i_3[i])
        obj.addTerms(params.c_li[i], pai_i_2[i])
        obj.addTerms(params.c_li[i], pai_i_4[i])
        obj.add(quicksum(params.z * gamma_ik[i, k] * (pai_ik_5[i, k] + pai_ik_6[i, k] + pai_ik_7[i, k]) for k in range(params.K1)))
    obj.add(quicksum(params.C*pai_t_8[t] for t in range(params.T)))
    model.setObjective(obj, GRB.MAXIMIZE)
    # endregion

    # region 定义约束条件
    for i in range(1,params.V+1):
        model.addConstr(pai_i_1[i] + pai_ih_9[i, 1] <= 0,name="dual_constrain_1_{}_{}".format(i,1))
        model.addConstr(pai_i_2[i] + pai_ih_10[i, 1] <= 0, name="dual_constrain_2_{}_{}".format(i, 1))
        model.addConstr(-pai_i_1[i] - pai_ih_9[i, 0] + pai_i_12[i] - pai_i_11[i] >= 0,name="dual_constrain_3_{}".format(i))
        model.addConstr(-pai_i_2[i] - pai_ih_10[i, 0] + pai_i_14[i] - pai_i_13[i] >= 0,name="dual_constrain_4_{}".format(i))
    for i in range(1,params.V+1):
        for k in range(params.K0):
            model.addConstr(params.t_lki[k, i] - pai_i_4[i] - pai_ik_5[i, k] - pai_ik_7[i, k] + pai_i_13[i] - pai_i_14[i] >= 0, name="dual_constrain_5_{}_{}".format(i, k))
            model.addConstr(params.t_uki[k, i] - pai_i_3[i] - pai_ik_5[i, k] - pai_ik_6[i, k] + pai_i_11[i] - pai_i_12[i] >= 0, name="dual_constrain_6_{}_{}".format(i,k))
        for k in range(params.K0,params.K1):
            model.addConstr(params.t_lki[k, i] - pai_i_4[i] - pai_ik_5[i, k] - pai_ik_7[i, k]  >= 0, name="dual_constrain_7_{}_{}".format(i, k))
            model.addConstr(params.t_uki[k, i] - pai_i_3[i] - pai_ik_5[i, k] - pai_ik_6[i, k]  >= 0, name="dual_constrain_8_{}_{}".format(i, k))
    for i in range(1,params.V+1):
        expr1 = quicksum(pai_ih_10[i,h] for h in range(params.H))
        expr2 = quicksum(pai_ih_9[i,h] for h in range(params.H))
        model.addConstr(params.time_p + expr1 >= 0,name="dual_constrain_9_{}".format(i))
        model.addConstr(params.time_p + expr2 >= 0,name="dual_constrain_10_{}".format(i))
        expr3 = LinExpr()
        for t in range(params.T):
            if params.l_it[i,t]==1:
                expr3.addTerms(1/params.handle_i[i],pai_t_8[t])
        model.addConstr(params.time_u + pai_i_13[i] + pai_i_14[i]-expr3 >= 0,name="dual_constrain_11_{}".format(i))
        model.addConstr(params.time_u + pai_i_11[i] + pai_i_12[i]-expr3 >= 0,name="dual_constrain_12_{}".format(i))
    # endregion

    model.update()

    model.optimize()
    time_end = time.time()
    print("DSP_model 耗时：{:.2f}s".format(time_end - time_start))
    print("模型的求解状态：", model.getAttr(GRB.attr.Status))
    if model.getAttr(GRB.attr.Status) in [2,9]:
        print("DSP - obj = {} ".format(model.ObjVal))
    else:
        print('Optimization was stopped with status =  %d' % model.getAttr(GRB.attr.Status))
        model.computeIIS()
        for c in model.getConstrs():
            if c.IISConstr:
                print('%s' % c.constrName)

def test_reload():
    test_ui = np.zeros(params.V + 2, dtype=int)  # 轮船i的unloading数量
    print("test.py 中  V = {} K ={}  G = {}  len(c_ui)={}  len(test_ui)={} seed={}".format(params.V, params.K1, params.G, len(params.c_ui),len(test_ui),params.seed))
    print(params.c_ui)